#pragma once
#include <iostream>
#include <vector>
#include <pthread.h>
#include <semaphore.h>

template<class T>
class RingQueue
{
    const static int defaultNum = 20;
    void P(sem_t& sem) { sem_wait(&sem); }
    void V(sem_t& sem) { sem_post(&sem); }
    void Lock(pthread_mutex_t& mutex) { pthread_mutex_lock(&mutex); }
    void UnLock(pthread_mutex_t& mutex) { pthread_mutex_unlock(&mutex); }
public:
    RingQueue(int capacity = defaultNum)
        :_ringQueue(capacity), _capacity(capacity), _cStep(0), _pStep(0)
    {
        sem_init(&_cdataSem, 0, 0);
        sem_init(&_pspaceSem, 0, _capacity);
        pthread_mutex_init(&_cMutex, nullptr);
        pthread_mutex_init(&_pMutex, nullptr);
    }

    void push(const T& in)
    {
        // 1.信号量的申请是原子的，不需要加锁
        // 2.先申请信号量，这一步是并发的，即便申请不到锁，等其它线程释放后，也可以立即申请锁（信号量已经申请到了）
        P(_pspaceSem); 
        Lock(_pMutex);  // 加锁保护生产内部的共享数据，即_pStep
        _ringQueue[_pStep++] = in;
        _pStep %= _capacity;    // 循环队列
        UnLock(_pMutex);
        V(_cdataSem);
    }

    void pop(T* out)
    {
        P(_cdataSem);
        Lock(_cMutex);
        *out = _ringQueue[_cStep++]; 
        _cStep %= _capacity;   
        UnLock(_cMutex);
        V(_pspaceSem);
    }

    ~RingQueue()
    {
        sem_destroy(&_cdataSem);
        sem_destroy(&_pspaceSem);
        pthread_mutex_destroy(&_cMutex);
        pthread_mutex_destroy(&_pMutex);
    }
private:
    std::vector<T> _ringQueue;
    int _capacity;
    int _cStep;
    int _pStep;
    sem_t _cdataSem;    // 消费信号量
    sem_t _pspaceSem;   // 生产信号量
    pthread_mutex_t _cMutex;
    pthread_mutex_t _pMutex;
};
